<?php

namespace Common\TagLib;

use Think\Template\TagLib;

defined('THINK_PATH') or exit();

/**
 * TagLibBase
 * TagLib项目基类
 * @since 1.0 2014-11-3 by sutroon
 * @example
 * 1.需要在配置文件config.php中增加：
 * 'TAGLIB_LOAD' => true, //加载标签库打开
 * 'APP_AUTOLOAD_PATH' => '@.TagLib',
 * 'TAGLIB_BUILD_IN' => 'Cx,dbhelper', // Cx为核心标签库名称,必需填写
 */
class TagBase extends TagLib {

    /**
     * 单条记录数组,用于_field方法
     * @var array
     */
    protected $data;

    /**
     * 解析变量并返回连贯查询脚本
     * @param $attr
     * @return string
     * @since 1.0 2014-11-1 by sutroon
     */
    function base_get_list_script(&$attr) {
        $table = isset($attr['table']) ? $attr['table'] : '';
        $where = isset($attr['where']) ? $attr['where'] : '';
        $where_static = isset($attr['where_static']) ? $attr['where_static'] : '';
        $field = isset($attr['field']) ? $attr['field'] : '';
        $order = isset($attr['order']) ? $attr['order'] : '';
        $limit = isset($attr['limit']) ? $attr['limit'] : '';
        $search = $this->tpl->get('search');
        $where_var = $this->tpl->get('where');
        if ($where_var) {
            $where = is_array($where_var) ? sofn_convert_array_search_to_string($where_var) : $where_var;
        }
        if ($where) {
            if ($where_static) {
                $where = $where_static . ' and ' . $where;
            }
        } else {
            $where = $where_static;
        }
        $attr['where'] = $where;
        // 分页列表
        if (isset($attr['paging']) && $attr['paging']) {
            $options_fields = array('pagesize', 'fields', 'orderby');
            foreach ($options_fields as $name) {
                if ($attr[$name]) {
                    $options[strtoupper($name)] = $attr[$name];
                }
            }
            if ($search) {
                $options['PAGEPARAMS'] = $search;
            }
            $strOptions = is_array($options) ? var_export($options, true) : 'null';
            ;
            return '$_list=get_paging_list(\'' . $table . '\', "' . $where . '", $page, ' . $strOptions . ');';
        }
        // 无分页列表
        $code = '$_list=M("' . $table . '")';
        if ($field) {
            $code .= '->field("' . $field . '")';
        }
        if ($where) {
            $code .= '->where("' . $where . '")';
        }
        if ($order) {
            $code .= '->order("' . $field . '")';
        }
        if ($limit) {
            $code .= '->limit(' . $limit . ')';
        }
        $code .= '->select();';
        return $code;
    }

    /**
     * 将attr转为控件属性字符串
     * @param array $attr 属性数据
     * @param array $skip 过滤的数据
     * @return string
     * @since 1.0 2014-11-3 by sutroon
     */
    function base_attr_to_string($attr, $skip) {
        $str = '';
        foreach ($attr as $k => $v) {
            if (is_array($skip) && in_array($k, $skip)) {
                continue;
            }
            $str .= ' ' . $k . '="' . $v . '"';
        }
        return $str;
    }

    /**
     * 解析select、radio、checkbox等标签的自循环属性 items="v1=txt1&val2=txt2" 或 min="1" max="5" step="1"生成的数组元素
     * @param $attr
     * @return array|false
     * @since 1.0 2014-11-7 by sutroon
     * @since 1.1 <2015-10-22> SoChishun 改进items属性，支持json参数
     * @since 1.2 <2015-12-02> SoChishun 新增items的模板变量数据源
     * @example 
     *  <sohtml:select items="v1=name1;v2=name2" />
     *  <sohtml:select name="level" items="=白金卡;=金卡;=银卡;=铜卡;=A;=B;=C" />
     */
    function base_parse_items_array($attr) {
        $min = isset($attr['min']) ? $attr['min'] : '';
        $max = isset($attr['max']) ? $attr['max'] : '';
        $step = isset($attr['step']) ? $attr['step'] : '';
        $items = isset($attr['items']) ? $attr['items'] : '';
        $skips = isset($attr['skips']) ? $attr['skips'] : ''; // 如 {$data['skips']}或 3,5,6
        if ($skips) {
            if ($skips[0] = '{') {
                $skips = $this->base_get_tpl_data($skips);
            } else {
                $skips = explode(',', $skips);
            }
        }
        // 动态循环数据源
        if ($max) {
            if (!$min) {
                $min = 1;
            }
            if (!$step) {
                $step = 1;
            }
            for ($i = $min; $i <= $max; $i += $step) {
                if ($skips && in_array($i, $skips)) {
                    continue;
                }
                $arr_items[$i] = $i;
            }
            return $arr_items;
        }
        // json数据源
        if ('[' == $items[0]) {
            return json_decode($items, true);
        }
        // 模板变量数据源
        if ('{' == $items[0]) {
            return $this->base_get_tpl_data($items);
        }
        // 静态文本数据源
        $arr_items = $this->base_string_params_to_array($items, '=', ';');
        if ($skips) {
            $arrTemp = $arr_items;
            foreach ($arrTemp as $key => $val) {
                if (in_array($key, $skips)) {
                    unset($arr_items[$key]);
                }
            }
        }
        return $arr_items;
    }

    /**
     * 将参数转换成数组
     * @param $str 参数,如 a=1&b=2&c=3
     * @param string $col 列分隔符,如 =
     * @param string $row 行分隔符,如 &
     * @return array|false 返回拆分后的数组或false
     * @since 1.0 2014-10-29 by sutroon; 
     * @example 
     *      $arr=sofn_string_params_to_array('a=1&b=2&c=3');
     *      $arr_items = sofn_string_params_to_array($items, '=', ';');
     */
    function base_string_params_to_array($str, $col = '=', $row = '&') {
        $arr = explode($row, $str);
        $arrOut = false;
        foreach ($arr as $item) {
            $arr2 = explode($col, $item);
            if (empty($arr2[0]) && $arr2[0] !== '0') {
                $arr2[0] = $arr2[1];
            }
            $arrOut[$arr2[0]] = $arr2[1];
        }
        return $arrOut;
    }

    /**
     * 获取模板变量的值
     * @param $name 变量名称
     * @param bool $simple 是否只从Data读取
     * @return string
     * @since 1.0 2014-11-4 by sutroon; 1.1 2014-11-6 by sutroon 新增简单名称模式; 1.3 2014-11-7 by sutroon 新增模板函数支持
     * @example
     * <user:field name="userName" />
     * <user:field name="{$data.userName}" />
     * <user:field name="{$data['userName']}" />
     * <sohtml:radio name="state" items="1=正常;0=禁用" value="{$data.state|default='1'}" />
     * <sohtml:radio name="state" items="1=正常;0=禁用" value="{$data.state|sofn_not_empty=###,'1'}" />
     */
    function base_get_tpl_data($name, $simpleName = false) {
        // 简单名称模式,如 id
        if ($simpleName) {
            if ($this->data) {
                $data = $this->data;
            } else {
                $data = $this->tpl->get('data');
                $this->data = $data;
            }
            return ($data && isset($data[$name])) ? $data[$name] : '';
        }
        // 复杂名称模式,如 {$data['id']}, {$data.id}
        if (strlen($name) < 5 || substr($name, 0, 2) !== '{$') {
            return $name;
        }
        $name = substr($name, 2, strlen($name) - 3);
        $data_name = false;
        $funcs = false;
        if (false !== ($pos = strpos($name, '['))) {
            $data_name = substr($name, 0, $pos);
            if (false !== ($posfunc = strpos($name, '|'))) {
                $funcs = substr($name, $posfunc + 1);
                $name = substr($name, $pos + 2, strpos($name, ']') - 7);
            } else {
                $name = substr($name, $pos + 2, strlen($name) - $pos - 4);
            }
        }
        if (!$data_name && false !== ($pos = strpos($name, '.'))) {
            $data_name = substr($name, 0, $pos);
            if (false !== ($posfunc = strpos($name, '|'))) {
                $funcs = substr($name, $posfunc + 1);
                $name = substr($name, $pos + 1, $posfunc - $pos - 1);
            } else {
                $name = substr($name, $pos + 1);
            }
        }
        if ($data_name) {
            if ($this->data) {
                $data = $this->data;
            } else {
                $data = $this->tpl->get($data_name);
                $this->data = $data;
            }
            // 函数处理
            if ($funcs) {
                $parseStr = $this->tpl->parseVar($name . '|' . $funcs);
                $parseStr = substr($parseStr, 11, strlen($parseStr) - 15);
                $$name = ($data && isset($data[$name])) ? $data[$name] : '';
                return @(eval('return (' . $parseStr . ');'));
            } else {
                return ($data && isset($data[$name])) ? $data[$name] : '';
            }
        } else {
            // 函数处理
            if (false !== ($pos = strpos($name, '|'))) {
                $parseStr = $this->tpl->parseVar($name);
                $code = substr($parseStr, 11, strlen($parseStr) - 15);
                $name = substr($name, 0, $pos);
                $$name = $this->tpl->get($name);
                return @(eval('return (' . $code . ');'));
            } else {
                return $this->tpl->get($name);
            }
        }
    }

    /**
     * 获取通用列表代码
     * @param $attr
     * @param $content
     * @return string
     */
    function base_list_script($attr, $content) {
        $item = empty($attr['item']) ? '$_row' : '$' . $attr['item'];
        $script = $this->base_get_list_script($attr);
        $str = '<?php ';
        $str .= $script;
        $str .= '$key=1;';
        $str .= 'foreach ($_list as ' . $item . '):';
        $str .= 'extract(' . $item . ');';
        $str .= '?>';
        $str .= $content;
        $str .= '<?php $key++;?>';
        $str .= '<?php endforeach ?>';
        return $str;
    }

    /**
     * 获取树形结果集
     * @param $attr
     * @param $content
     * @since 1.0 2014-11-1 by sutroon
     * @example
     * <taglib name="dbhelper"/>
     * <ul>
     *   <dbhelper:treeset table="tgeneral_catalog" orderby="id">
     *      <li><a href="{:U('Catalog/item_edit','id='.$id)}">{$catalogName}</a>
     *          <ul>
     *              <foreach name="sub" item="vo">
     *                  <li><a href="{:U('Catalog/item_edit','id='.$vo['id'])}">----{$vo.catalogName}</a>
     *                      <ul>
     *                          <foreach name="vo.sub" item="vo3">
     *                              <li><a href="{:U('Catalog/item_edit','id='.$vo3['id'])}">--------{$vo3.catalogName}</a>
     *                                  <ul>
     *                                      <foreach name="vo3.sub" item="vo4">
     *                                          <li><a href="{:U('Catalog/item_edit','id='.$vo4['id'])}">------------{$vo4.catalogName}</a></li>
     *                                      </foreach>
     *                                  </ul>
     *                              </li>
     *                          </foreach>
     *                      </ul>
     *                  </li>
     *              </foreach>
     *          </ul>
     *      </li>
     *   </dbhelper:treeset>
     * </ul>
     */
    public function base_treeset_script($attr, $content) {
        $table = isset($attr['table']) ? $attr['table'] : '';
        $where = isset($attr['where']) ? $attr['where'] : '';
        $parentID = isset($attr['parentID']) ? $attr['parentID'] : '';
        $orderby = isset($attr['orderby']) ? $attr['orderby'] : '';
        if (!$parentID) {
            $parentID = 0;
        }
        if (!$where) {
            $where = 'parentID={0}';
        }
        $options = null;
        if ($orderby) {
            $options['ORDERBY'] = $orderby;
        }
        $script = '$_list=sofn_get_tree_table(\'' . $table . '\',"' . $where . '",' . $parentID . ',' . (is_array($options) ? var_export($options, true) : 'array()') . ');';
        $str = '<?php ';
        $str .= $script;
        $str .= 'foreach ($_list as $_row):';
        $str .= 'extract($_row);?>';
        $str .= $content;
        $str .= '<?php endforeach ?>';
        return $str;
    }

    /**
     * 获取树形Select控件
     * @param $attr
     * @param $content
     * @return string
     * @since 1.0 2014-11-3 by sutroon
     * @example
     *  <taglib name="dbhelper"/>
     *  <dbhelper:treeselect table="tgeneral_catalog" orderby="id" value="3" />
     */
    public function base_treeselect($attr) {
        $table = isset($attr['table']) ? $attr['table'] : '';
        $where = isset($attr['where']) ? $attr['where'] : '';
        $parentID = isset($attr['parent']) ? $attr['parent'] : '';
        $orderby = isset($attr['orderby']) ? $attr['orderby'] : '';
        $default = isset($attr['default']) ? $attr['default'] : '';
        $value = isset($attr['value']) ? $this->base_get_tpl_data($attr['value']) : $default;

        if (!$parentID) {
            $parentID = 0;
        }
        if (!$where) {
            $where = 'parentID={0}';
            $siteid = isset($attr['siteid']) ? $this->base_get_tpl_data($attr['siteid']) : '';
            if (strlen($siteid) > 0) {
                $where = "siteID=$siteid and $where";
            }
            if (isset($attr['extags'])) {
                $where = "exTags='" . $attr['extags'] . "' and $where";
            }
        }
        if ($orderby) {
            $options['ORDERBY'] = $orderby;
        }
        $list = sofn_get_tree_table($table, $where, $parentID, $options);
        return '<select' . $this->base_attr_to_string($attr, array('table', 'where', 'parentid', 'order', 'value', 'siteid', 'extags')) . '><option value="">请选择</option>' . sofn_get_tree_option_from_treetable($list, $value) . '</select>';
    }

    /**
     * 树形控件
     * @param $attr
     * @param $content
     * @return string
     * @since 1.0 2014-11-5 by sutroon 由AdminAction.itemTreeList()(2014-10-28)迁移过来
     * @example
     *  <taglib name="dbhelper"/>
     *  <dbhelper:treelist table="tuser_permission" checkbox="true" values="{$values}" />
     *  $this->assign('values', M('tuser_member')->where('id=' . $uid)->getField('permissionRule'));
     */
    function base_treelist($attr) {
        $table = isset($attr['table']) ? $attr['table'] : '';
        $where = isset($attr['where']) ? $attr['where'] : ''; // string $where 查询条件,如: parentID={0},占位符{0}
        $parentID = isset($attr['parentID']) ? $attr['parentID'] : ''; // int $parentID 父级编号
        $orderby = isset($attr['orderby']) ? $attr['orderby'] : '';
        $values = isset($attr['values']) ? $attr['values'] : '';
        if ($values) {
            $values = $this->base_get_tpl_data($values);
        }
        $checkbox = isset($attr['checkbox']) ? $attr['checkbox'] : '';

        if (!$parentID) {
            $parentID = 0;
        }
        if (!$where) {
            $where = $this->tpl->get('where');
        }
        if (!$where) {
            $where = 'parentID={0}';
        }
        $options = array(); // array $options 可选参数, 包含: string:FIELDS, string:ORDERBY, bool:CHECKBOX, string:VALUES
        if ($orderby) {
            $options['ORDERBY'] = $orderby;
        }
        if ($values) {
            $options['VALUES'] = $values;
        }
        if ($checkbox) {
            $options['CHECKBOX'] = $checkbox;
        }
        $list = sofn_get_tree_table($table, $where, $parentID, $options);
        if ($list) {
            $str = $this->func_treeList($list, $values, $checkbox);
            $str = '<ul class="ul-tree">' . substr($str, 4);
        } else {
            $str = '';
        }
        return $str;
    }

    // treelist内部递归方法 2014-10-28 by sutroon
    private function func_treeList($list, $checked_values, $checkbox = false) {
        $str = '<ul>';
        $id_name = '';
        $value_name = '';
        foreach ($list as $row) {
            if (!$id_name) {
                $keys = array_keys($row);
                $id_name = $keys[0];
                $value_name = $keys[1];
            }
            $str .= '<li>';
            if ($checkbox) {
                $str .= '<input type="checkbox" name="ids[]" value="' . $row[$id_name] . '"' . ($checked_values && false !== strpos(',' . $checked_values . ',', ',' . $row[$id_name] . ',') ? ' checked="checked"' : '') . ' />';
            }
            $str .= '<strong>' . $row[$value_name] . '</strong>';
            if (isset($row['sub']) && $row['sub']) {
                $str .= $this->func_treeList($row['sub'], $checked_values, $checkbox);
            }
            $str .= '</li>';
        }
        $str .= '</ul>';
        return $str;
    }

}
